---
title: DOM 操作与事件处理
description: 通过掌握 DOM 操作和事件处理来创建动态、交互式用户界面，从后端逻辑转向前端用户交互。
---

# DOM 操作与事件处理

作为 Python 开发者，你习惯于处理数据结构和算法。在前端开发中，文档对象模型（DOM）成为你创建交互式用户界面的主要数据结构。本模块教你如何有效地操作 DOM 和处理用户事件。

## 理解 DOM

### 从 Python 数据结构到 DOM

<PythonEditor title="数据结构操作对比" compare={true}>
```python !! py
# Python：处理数据结构（列表、字典）
import json
from datetime import datetime

class TaskManager:
    """
    Python 方式：处理原生数据结构
    - 直接操作列表和字典
    - 默认无视觉表示
    - 专注于数据逻辑和算法
    """
    
    def __init__(self):
        self.tasks = []
        self.categories = {}
    
    def add_task(self, title, description, category="general"):
        """向数据结构添加新任务"""
        task = {
            "id": len(self.tasks) + 1,
            "title": title,
            "description": description,
            "category": category,
            "completed": False,
            "created_at": datetime.now().isoformat()
        }
        
        # 添加到主列表
        self.tasks.append(task)
        
        # 更新分类
        if category not in self.categories:
            self.categories[category] = []
        self.categories[category].append(task["id"])
        
        print(f"任务已添加：{task['title']}")
        return task
    
    def complete_task(self, task_id):
        """标记任务为已完成"""
        for task in self.tasks:
            if task["id"] == task_id:
                task["completed"] = True
                task["completed_at"] = datetime.now().isoformat()
                print(f"任务已完成：{task['title']}")
                return True
        return False
    
    def filter_tasks(self, completed=None, category=None):
        """根据条件过滤任务"""
        filtered = self.tasks
        
        if completed is not None:
            filtered = [t for t in filtered if t["completed"] == completed]
        
        if category:
            filtered = [t for t in filtered if t["category"] == category]
        
        return filtered
    
    def display_tasks(self):
        """在控制台显示任务"""
        print("\n=== 任务列表 ===")
        for task in self.tasks:
            status = "✓" if task["completed"] else "○"
            print(f"{status} [{task['category']}] {task['title']}")
            print(f"   {task['description']}")
        print("===============")

# Python 用法：在内存中处理数据
task_manager = TaskManager()

# 添加一些任务
task_manager.add_task("学习 JavaScript", "掌握前端开发", "教育")
task_manager.add_task("构建网站", "创建个人作品集", "项目")
task_manager.add_task("学习算法", "准备面试", "教育")

# 操作数据
task_manager.complete_task(1)

# 过滤和显示
education_tasks = task_manager.filter_tasks(category="教育")
print(f"教育类任务：{len(education_tasks)} 个")

task_manager.display_tasks()
```

```javascript !! js
// JavaScript：处理 DOM（文档对象模型）
// DOM 是表示 HTML 文档的活动数据结构

class TaskDOMManager {
    /**
     * JavaScript 方式：处理 DOM 元素
     * - 内置视觉表示
     * - 元素具有数据和视觉属性
     * - 更改立即反映在用户界面中
     */
    
    constructor() {
        this.tasks = [];
        this.taskContainer = document.getElementById('tasks-container');
        this.taskForm = document.getElementById('task-form');
        this.setupEventListeners();
        this.initializeDOM();
    }
    
    initializeDOM() {
        // 如果不存在，创建初始 DOM 结构
        if (!this.taskContainer) {
            this.createTaskContainer();
        }
        if (!this.taskForm) {
            this.createTaskForm();
        }
    }
    
    createTaskContainer() {
        // 程序化创建 DOM 元素
        this.taskContainer = document.createElement('div');
        this.taskContainer.id = 'tasks-container';
        this.taskContainer.className = 'tasks-list';
        
        // 添加到文档
        document.body.appendChild(this.taskContainer);
    }
    
    createTaskForm() {
        // 创建添加任务的表单
        this.taskForm = document.createElement('form');
        this.taskForm.id = 'task-form';
        this.taskForm.innerHTML = `
            <div class="form-group">
                <input type="text" id="task-title" placeholder="任务标题" required>
                <input type="text" id="task-description" placeholder="描述">
                <select id="task-category">
                    <option value="general">常规</option>
                    <option value="education">教育</option>
                    <option value="project">项目</option>
                </select>
                <button type="submit">添加任务</button>
            </div>
        `;
        
        // 插入到 body 开头
        document.body.insertBefore(this.taskForm, this.taskContainer);
    }
    
    addTask(title, description, category = "general") {
        /**
         * 添加任务：数据和 DOM 操作
         * - 存储在数据结构中（如 Python）
         * - 创建视觉表示（前端独有）
         */
        const task = {
            id: Date.now(), // 简单的 ID 生成
            title: title,
            description: description,
            category: category,
            completed: false,
            createdAt: new Date().toISOString()
        };
        
        // 添加到数据结构
        this.tasks.push(task);
        
        // 为任务创建 DOM 元素
        const taskElement = this.createTaskElement(task);
        
        // 添加到 DOM 并带有动画
        this.taskContainer.appendChild(taskElement);
        
        // 新任务出现动画
        taskElement.style.opacity = '0';
        taskElement.style.transform = 'translateY(-20px)';
        
        // 触发动画
        requestAnimationFrame(() => {
            taskElement.style.transition = 'all 0.3s ease';
            taskElement.style.opacity = '1';
            taskElement.style.transform = 'translateY(0)';
        });
        
        console.log(`任务已添加到 DOM：${task.title}`);
        return task;
    }
    
    createTaskElement(task) {
        // 创建任务的 DOM 表示
        const taskDiv = document.createElement('div');
        taskDiv.className = `task-item ${task.completed ? 'completed' : ''}`;
        taskDiv.dataset.taskId = task.id; // 在 DOM 中存储 ID
        
        taskDiv.innerHTML = `
            <div class="task-header">
                <span class="task-category">${task.category}</span>
                <button class="complete-btn" data-action="complete">
                    ${task.completed ? '✓' : '○'}
                </button>
            </div>
            <h3 class="task-title">${task.title}</h3>
            <p class="task-description">${task.description}</p>
            <div class="task-actions">
                <button class="edit-btn" data-action="edit">编辑</button>
                <button class="delete-btn" data-action="delete">删除</button>
            </div>
        `;
        
        return taskDiv;
    }
    
    completeTask(taskId) {
        // 更新数据和 DOM
        const task = this.tasks.find(t => t.id == taskId);
        if (!task) return false;
        
        // 更新数据
        task.completed = !task.completed;
        task.completedAt = task.completed ? new Date().toISOString() : null;
        
        // 更新 DOM 元素
        const taskElement = document.querySelector(`[data-task-id="${taskId}"]`);
        if (taskElement) {
            // 更新视觉状态
            taskElement.classList.toggle('completed', task.completed);
            
            // 更新按钮文本
            const completeBtn = taskElement.querySelector('.complete-btn');
            completeBtn.textContent = task.completed ? '✓' : '○';
            
            // 添加完成动画
            if (task.completed) {
                taskElement.style.transform = 'scale(1.05)';
                setTimeout(() => {
                    taskElement.style.transform = 'scale(1)';
                }, 200);
            }
        }
        
        console.log(`任务${task.completed ? '已完成' : '已重新开启'}：${task.title}`);
        return true;
    }
    
    deleteTask(taskId) {
        // 从数据和 DOM 中移除
        const taskIndex = this.tasks.findIndex(t => t.id == taskId);
        if (taskIndex === -1) return false;
        
        // 从数据中移除
        const task = this.tasks.splice(taskIndex, 1)[0];
        
        // 从 DOM 中移除并带有动画
        const taskElement = document.querySelector(`[data-task-id="${taskId}"]`);
        if (taskElement) {
            // 移除动画
            taskElement.style.transition = 'all 0.3s ease';
            taskElement.style.opacity = '0';
            taskElement.style.transform = 'translateX(-100%)';
            
            // 动画后移除
            setTimeout(() => {
                taskElement.remove();
            }, 300);
        }
        
        console.log(`任务已删除：${task.title}`);
        return true;
    }
    
    filterTasks(completed = null, category = null) {
        // 过滤任务并更新 DOM 显示
        const allTaskElements = document.querySelectorAll('.task-item');
        
        allTaskElements.forEach(element => {
            const taskId = element.dataset.taskId;
            const task = this.tasks.find(t => t.id == taskId);
            
            let shouldShow = true;
            
            // 应用过滤器
            if (completed !== null && task.completed !== completed) {
                shouldShow = false;
            }
            
            if (category && task.category !== category) {
                shouldShow = false;
            }
            
            // 带动画显示/隐藏
            if (shouldShow) {
                element.style.display = 'block';
                element.style.opacity = '1';
            } else {
                element.style.opacity = '0.3';
                element.style.display = 'none';
            }
        });
    }
    
    setupEventListeners() {
        // 设置表单提交
        document.addEventListener('submit', (event) => {
            if (event.target.id === 'task-form') {
                event.preventDefault();
                this.handleFormSubmit(event);
            }
        });
        
        // 使用事件委托设置任务交互
        document.addEventListener('click', (event) => {
            const action = event.target.dataset.action;
            const taskElement = event.target.closest('.task-item');
            
            if (!taskElement) return;
            
            const taskId = taskElement.dataset.taskId;
            
            switch (action) {
                case 'complete':
                    this.completeTask(taskId);
                    break;
                case 'edit':
                    this.editTask(taskId);
                    break;
                case 'delete':
                    this.deleteTask(taskId);
                    break;
            }
        });
    }
    
    handleFormSubmit(event) {
        const title = document.getElementById('task-title').value;
        const description = document.getElementById('task-description').value;
        const category = document.getElementById('task-category').value;
        
        if (title.trim()) {
            this.addTask(title, description, category);
            event.target.reset(); // 清空表单
        }
    }
    
    editTask(taskId) {
        // 内联编辑功能
        const taskElement = document.querySelector(`[data-task-id="${taskId}"]`);
        const task = this.tasks.find(t => t.id == taskId);
        
        if (!taskElement || !task) return;
        
        const titleElement = taskElement.querySelector('.task-title');
        const descriptionElement = taskElement.querySelector('.task-description');
        
        // 使元素可编辑
        titleElement.contentEditable = true;
        descriptionElement.contentEditable = true;
        titleElement.focus();
        
        // 添加编辑样式
        titleElement.classList.add('editing');
        descriptionElement.classList.add('editing');
        
        // 处理 Enter 键或失焦时保存
        const saveEdit = () => {
            task.title = titleElement.textContent.trim();
            task.description = descriptionElement.textContent.trim();
            
            titleElement.contentEditable = false;
            descriptionElement.contentEditable = false;
            titleElement.classList.remove('editing');
            descriptionElement.classList.remove('editing');
            
            console.log(`任务已编辑：${task.title}`);
        };
        
        titleElement.addEventListener('blur', saveEdit, { once: true });
        titleElement.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                e.preventDefault();
                saveEdit();
            }
        }, { once: true });
    }
}

// 初始化基于 DOM 的任务管理器
const domTaskManager = new TaskDOMManager();

// 添加一些示例任务
domTaskManager.addTask("学习 DOM 操作", "掌握 JavaScript DOM API", "教育");
domTaskManager.addTask("构建交互式界面", "创建动态用户界面", "项目");
domTaskManager.addTask("处理用户事件", "实现事件监听器", "教育");

// JavaScript DOM 特征：
// 1. 视觉和数据表示结合
// 2. 用户界面实时更新
// 3. 事件驱动的用户交互
// 4. 动画和视觉反馈
console.log("JavaScript：DOM 操作带有视觉反馈和实时更新");
```
</PythonEditor>

## 事件驱动的用户交互

### 从函数调用到事件处理器

<PythonEditor title="用户交互模式" compare={true}>
```python !! py
# Python：基于函数的交互（CLI/程序化）
import cmd
import sys
from datetime import datetime

class TaskCLI(cmd.Cmd):
    """
    Python 方式：命令行界面
    - 顺序命令处理
    - 基于文本的用户交互
    - 无并发用户操作
    """
    
    intro = '欢迎使用任务管理器 CLI。输入 help 或 ? 查看命令列表。\n'
    prompt = '(任务) '
    
    def __init__(self):
        super().__init__()
        self.tasks = []
        self.current_id = 1
    
    def do_add(self, line):
        """添加新任务：add <标题> | <描述>"""
        try:
            parts = line.split(' | ')
            title = parts[0].strip()
            description = parts[1].strip() if len(parts) > 1 else ""
            
            task = {
                'id': self.current_id,
                'title': title,
                'description': description,
                'completed': False,
                'created': datetime.now()
            }
            
            self.tasks.append(task)
            self.current_id += 1
            print(f"任务已添加：{title}")
            
        except Exception as e:
            print(f"错误：{e}")
    
    def do_list(self, line):
        """列出所有任务"""
        if not self.tasks:
            print("未找到任务。")
            return
        
        print("\n=== 任务列表 ===")
        for task in self.tasks:
            status = "✓" if task['completed'] else "○"
            print(f"{task['id']}. {status} {task['title']}")
            if task['description']:
                print(f"   {task['description']}")
        print("===============\n")
    
    def do_complete(self, line):
        """标记任务为已完成：complete <id>"""
        try:
            task_id = int(line.strip())
            task = next((t for t in self.tasks if t['id'] == task_id), None)
            
            if task:
                task['completed'] = True
                print(f"任务已完成：{task['title']}")
            else:
                print(f"未找到任务 {task_id}。")
                
        except ValueError:
            print("请提供有效的任务 ID。")
    
    def do_delete(self, line):
        """删除任务：delete <id>"""
        try:
            task_id = int(line.strip())
            task = next((t for t in self.tasks if t['id'] == task_id), None)
            
            if task:
                self.tasks.remove(task)
                print(f"任务已删除：{task['title']}")
            else:
                print(f"未找到任务 {task_id}。")
                
        except ValueError:
            print("请提供有效的任务 ID。")
    
    def do_quit(self, line):
        """退出程序"""
        print("再见！")
        return True
    
    def do_EOF(self, line):
        """处理 Ctrl+D"""
        return self.do_quit(line)

# Python CLI 用法
if __name__ == "__main__":
    print("Python CLI 任务管理器：")
    print("- 顺序命令处理")
    print("- 基于文本的交互")
    print("- 单线程执行")
    
    # 这将启动 CLI 循环
    # TaskCLI().cmdloop()
    
    # 模拟交互
    cli = TaskCLI()
    print("\n模拟 CLI 命令：")
    cli.do_add("学习 Python | 掌握后端开发")
    cli.do_add("构建 API | 创建 REST API")
    cli.do_list("")
    cli.do_complete("1")
    cli.do_list("")
```

```javascript !! js
// JavaScript：事件驱动用户界面
// 多个并发用户交互

class InteractiveTaskManager {
    /**
     * JavaScript 方式：事件驱动界面
     * - 多个同时的用户交互
     * - 所有操作的视觉反馈
     * - 异步事件处理
     */
    
    constructor() {
        this.tasks = [];
        this.currentId = 1;
        this.isEditing = false;
        this.setupInterface();
        this.setupEventListeners();
        this.setupKeyboardShortcuts();
    }
    
    setupInterface() {
        // 创建丰富的交互式界面
        document.body.innerHTML = `
            <div class="task-app">
                <header class="app-header">
                    <h1>交互式任务管理器</h1>
                    <div class="quick-stats">
                        <span id="total-tasks">0 个任务</span>
                        <span id="completed-tasks">0 个已完成</span>
                    </div>
                </header>
                
                <div class="add-task-section">
                    <form id="task-form" class="task-form">
                        <input type="text" id="task-title" placeholder="需要做什么？" autocomplete="off">
                        <input type="text" id="task-description" placeholder="添加描述（可选）" autocomplete="off">
                        <button type="submit" class="add-btn">添加任务</button>
                    </form>
                </div>
                
                <div class="filter-section">
                    <button class="filter-btn active" data-filter="all">全部</button>
                    <button class="filter-btn" data-filter="pending">待办</button>
                    <button class="filter-btn" data-filter="completed">已完成</button>
                </div>
                
                <div id="tasks-container" class="tasks-container">
                    <div class="empty-state">
                        <p>还没有任务。在上面添加一个吧！</p>
                    </div>
                </div>
                
                <div class="bulk-actions">
                    <button id="select-all-btn">全选</button>
                    <button id="complete-selected-btn">完成选中</button>
                    <button id="delete-selected-btn">删除选中</button>
                </div>
            </div>
        `;
        
        // 添加交互性的 CSS 样式
        this.addStyles();
    }
    
    setupEventListeners() {
        // 表单提交 - 单个任务添加
        document.getElementById('task-form').addEventListener('submit', (e) => {
            e.preventDefault();
            this.handleTaskSubmission();
        });
        
        // 实时输入验证
        document.getElementById('task-title').addEventListener('input', (e) => {
            this.validateInput(e.target);
        });
        
        // 过滤按钮
        document.querySelectorAll('.filter-btn').forEach(btn => {
            btn.addEventListener('click', (e) => {
                this.handleFilterChange(e.target.dataset.filter);
            });
        });
        
        // 批量操作
        document.getElementById('select-all-btn').addEventListener('click', () => {
            this.selectAllTasks();
        });
        
        document.getElementById('complete-selected-btn').addEventListener('click', () => {
            this.completeSelectedTasks();
        });
        
        document.getElementById('delete-selected-btn').addEventListener('click', () => {
            this.deleteSelectedTasks();
        });
        
        // 动态任务元素的事件委托
        document.getElementById('tasks-container').addEventListener('click', (e) => {
            this.handleTaskInteraction(e);
        });
        
        // 双击编辑（按钮的替代方式）
        document.getElementById('tasks-container').addEventListener('dblclick', (e) => {
            if (e.target.classList.contains('task-title')) {
                this.enableInlineEditing(e.target);
            }
        });
        
        // 拖放重新排序
        document.getElementById('tasks-container').addEventListener('dragstart', (e) => {
            this.handleDragStart(e);
        });
        
        document.getElementById('tasks-container').addEventListener('dragover', (e) => {
            this.handleDragOver(e);
        });
        
        document.getElementById('tasks-container').addEventListener('drop', (e) => {
            this.handleDrop(e);
        });
    }
    
    setupKeyboardShortcuts() {
        // 全局键盘快捷键
        document.addEventListener('keydown', (e) => {
            // Ctrl/Cmd + Enter：快速添加任务
            if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
                document.getElementById('task-title').focus();
            }
            
            // Escape：取消当前操作
            if (e.key === 'Escape') {
                this.cancelCurrentOperation();
            }
            
            // Delete 键：删除选中的任务
            if (e.key === 'Delete' && !this.isEditing) {
                this.deleteSelectedTasks();
            }
            
            // Ctrl/Cmd + A：全选任务
            if ((e.ctrlKey || e.metaKey) && e.key === 'a' && !this.isEditing) {
                e.preventDefault();
                this.selectAllTasks();
            }
        });
    }
    
    handleTaskSubmission() {
        const titleInput = document.getElementById('task-title');
        const descriptionInput = document.getElementById('task-description');
        
        const title = titleInput.value.trim();
        const description = descriptionInput.value.trim();
        
        if (title) {
            this.addTask(title, description);
            titleInput.value = '';
            descriptionInput.value = '';
            titleInput.focus();
        }
    }
    
    addTask(title, description = '') {
        const task = {
            id: this.currentId++,
            title: title,
            description: description,
            completed: false,
            selected: false,
            createdAt: new Date(),
            order: this.tasks.length
        };
        
        this.tasks.push(task);
        this.renderTask(task);
        this.updateStats();
        this.updateEmptyState();
        
        // 显示成功反馈
        this.showNotification(`任务"${title}"添加成功！`, 'success');
        
        console.log(`任务已添加：${title}`);
    }
    
    renderTask(task) {
        const tasksContainer = document.getElementById('tasks-container');
        
        const taskElement = document.createElement('div');
        taskElement.className = `task-item ${task.completed ? 'completed' : ''}`;
        taskElement.dataset.taskId = task.id;
        taskElement.draggable = true;
        
        taskElement.innerHTML = `
            <div class="task-content">
                <input type="checkbox" class="task-checkbox" ${task.selected ? 'checked' : ''}>
                <div class="task-info">
                    <h3 class="task-title" contenteditable="false">${task.title}</h3>
                    ${task.description ? `<p class="task-description">${task.description}</p>` : ''}
                </div>
                <div class="task-actions">
                    <button class="complete-btn" data-action="toggle-complete" title="切换完成状态">
                        ${task.completed ? '✓' : '○'}
                    </button>
                    <button class="edit-btn" data-action="edit" title="编辑任务">✎</button>
                    <button class="delete-btn" data-action="delete" title="删除任务">🗑</button>
                </div>
            </div>
        `;
        
        // 添加动画
        taskElement.style.opacity = '0';
        taskElement.style.transform = 'translateY(-20px)';
        tasksContainer.appendChild(taskElement);
        
        // 触发动画
        requestAnimationFrame(() => {
            taskElement.style.transition = 'all 0.3s ease';
            taskElement.style.opacity = '1';
            taskElement.style.transform = 'translateY(0)';
        });
    }
    
    handleTaskInteraction(event) {
        const taskElement = event.target.closest('.task-item');
        if (!taskElement) return;
        
        const taskId = parseInt(taskElement.dataset.taskId);
        const action = event.target.dataset.action;
        
        switch (action) {
            case 'toggle-complete':
                this.toggleTaskCompletion(taskId);
                break;
            case 'edit':
                this.enableInlineEditing(taskElement.querySelector('.task-title'));
                break;
            case 'delete':
                this.deleteTask(taskId);
                break;
        }
        
        // 处理复选框选择
        if (event.target.type === 'checkbox') {
            this.toggleTaskSelection(taskId);
        }
    }
    
    toggleTaskCompletion(taskId) {
        const task = this.tasks.find(t => t.id === taskId);
        if (!task) return;
        
        task.completed = !task.completed;
        
        const taskElement = document.querySelector(`[data-task-id="${taskId}"]`);
        taskElement.classList.toggle('completed', task.completed);
        
        const completeBtn = taskElement.querySelector('.complete-btn');
        completeBtn.textContent = task.completed ? '✓' : '○';
        
        // 动画反馈
        taskElement.style.transform = 'scale(1.05)';
        setTimeout(() => {
            taskElement.style.transform = 'scale(1)';
        }, 150);
        
        this.updateStats();
        this.showNotification(
            `任务"${task.title}"${task.completed ? '已完成' : '已重新开启'}！`,
            task.completed ? 'success' : 'info'
        );
    }
    
    enableInlineEditing(titleElement) {
        if (this.isEditing) return;
        
        this.isEditing = true;
        const originalTitle = titleElement.textContent;
        
        titleElement.contentEditable = true;
        titleElement.classList.add('editing');
        titleElement.focus();
        
        // 选择所有文本
        const range = document.createRange();
        range.selectNodeContents(titleElement);
        const selection = window.getSelection();
        selection.removeAllRanges();
        selection.addRange(range);
        
        const saveEdit = () => {
            const newTitle = titleElement.textContent.trim();
            if (newTitle && newTitle !== originalTitle) {
                const taskId = parseInt(titleElement.closest('.task-item').dataset.taskId);
                const task = this.tasks.find(t => t.id === taskId);
                if (task) {
                    task.title = newTitle;
                    this.showNotification('任务更新成功！', 'success');
                }
            } else if (!newTitle) {
                titleElement.textContent = originalTitle;
            }
            
            titleElement.contentEditable = false;
            titleElement.classList.remove('editing');
            this.isEditing = false;
        };
        
        const cancelEdit = () => {
            titleElement.textContent = originalTitle;
            titleElement.contentEditable = false;
            titleElement.classList.remove('editing');
            this.isEditing = false;
        };
        
        // Enter 键保存，Escape 键取消
        titleElement.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                e.preventDefault();
                saveEdit();
            } else if (e.key === 'Escape') {
                e.preventDefault();
                cancelEdit();
            }
        });
        
        // 失焦时保存
        titleElement.addEventListener('blur', saveEdit, { once: true });
    }
    
    handleFilterChange(filter) {
        // 更新活动过滤按钮
        document.querySelectorAll('.filter-btn').forEach(btn => {
            btn.classList.remove('active');
        });
        document.querySelector(`[data-filter="${filter}"]`).classList.add('active');
        
        // 过滤任务
        document.querySelectorAll('.task-item').forEach(taskElement => {
            const taskId = parseInt(taskElement.dataset.taskId);
            const task = this.tasks.find(t => t.id === taskId);
            
            let shouldShow = true;
            
            switch (filter) {
                case 'pending':
                    shouldShow = !task.completed;
                    break;
                case 'completed':
                    shouldShow = task.completed;
                    break;
                case 'all':
                default:
                    shouldShow = true;
                    break;
            }
            
            taskElement.style.display = shouldShow ? 'block' : 'none';
        });
    }
    
    updateStats() {
        const total = this.tasks.length;
        const completed = this.tasks.filter(t => t.completed).length;
        
        document.getElementById('total-tasks').textContent = `${total} 个任务`;
        document.getElementById('completed-tasks').textContent = `${completed} 个已完成`;
    }
    
    updateEmptyState() {
        const emptyState = document.querySelector('.empty-state');
        const hasTasks = this.tasks.length > 0;
        emptyState.style.display = hasTasks ? 'none' : 'block';
    }
    
    showNotification(message, type = 'info') {
        // 创建并显示通知
        const notification = document.createElement('div');
        notification.className = `notification ${type}`;
        notification.textContent = message;
        
        document.body.appendChild(notification);
        
        // 3秒后自动移除
        setTimeout(() => {
            notification.remove();
        }, 3000);
    }
    
    addStyles() {
        const style = document.createElement('style');
        style.textContent = `
            .task-app { max-width: 800px; margin: 0 auto; padding: 20px; font-family: Arial, sans-serif; }
            .app-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 30px; }
            .quick-stats span { margin-left: 20px; color: #666; }
            .task-form { display: flex; gap: 10px; margin-bottom: 20px; }
            .task-form input { flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 4px; }
            .add-btn { padding: 10px 20px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; }
            .filter-section { display: flex; gap: 10px; margin-bottom: 20px; }
            .filter-btn { padding: 8px 16px; border: 1px solid #ddd; background: white; cursor: pointer; border-radius: 4px; }
            .filter-btn.active { background: #007bff; color: white; }
            .task-item { background: white; border: 1px solid #ddd; border-radius: 4px; margin-bottom: 10px; padding: 15px; transition: all 0.3s ease; }
            .task-item.completed { opacity: 0.7; }
            .task-content { display: flex; align-items: center; gap: 15px; }
            .task-info { flex: 1; }
            .task-title { margin: 0; cursor: text; }
            .task-title.editing { background: #f9f9f9; padding: 5px; border-radius: 3px; }
            .task-actions { display: flex; gap: 5px; }
            .task-actions button { background: none; border: none; cursor: pointer; padding: 5px; border-radius: 3px; }
            .task-actions button:hover { background: #f0f0f0; }
            .empty-state { text-align: center; color: #666; padding: 40px; }
            .bulk-actions { margin-top: 20px; display: flex; gap: 10px; }
            .bulk-actions button { padding: 8px 16px; border: 1px solid #ddd; background: white; cursor: pointer; border-radius: 4px; }
            .notification { position: fixed; top: 20px; right: 20px; padding: 15px; border-radius: 4px; color: white; z-index: 1000; }
            .notification.success { background: #28a745; }
            .notification.info { background: #17a2b8; }
            .notification.error { background: #dc3545; }
        `;
        document.head.appendChild(style);
    }
}

// 初始化交互式任务管理器
const interactiveTaskManager = new InteractiveTaskManager();

// 添加一些示例任务
interactiveTaskManager.addTask("学习 DOM 事件", "掌握 JavaScript 事件处理");
interactiveTaskManager.addTask("构建交互式界面", "创建响应式用户界面");
interactiveTaskManager.addTask("处理用户输入", "处理表单和用户交互");

// JavaScript 事件驱动特征：
// 1. 多个并发用户交互
// 2. 实时视觉反馈
// 3. 异步事件处理
// 4. 丰富的用户界面元素
console.log("JavaScript：事件驱动界面，具有实时交互和视觉反馈");
```
</PythonEditor>

## 总结和关键要点

DOM 操作和事件处理是创建交互式 Web 应用程序的基本技能：

### 🔄 **核心概念**
- **DOM 作为数据结构**：DOM 是一个活动的、可视化的数据结构，你可以像操作 Python 列表和字典一样操作它
- **事件驱动编程**：用户交互触发异步执行代码的事件
- **视觉反馈**：每个数据更改都可以立即反映在用户界面中

### 🛠 **基本技术**
- **元素创建和操作**：程序化创建和修改 HTML 元素
- **事件监听器**：响应用户操作，如点击、键盘输入和表单提交
- **事件委托**：使用事件冒泡高效处理动态内容的事件

### 📱 **用户体验增强**
- **实时更新**：为用户操作提供即时反馈
- **交互式元素**：创建具有动画和过渡效果的引人入胜的界面
- **无障碍性**：确保你的界面适用于所有用户，提供适当的键盘支持

在下一个模块中，我们将探索 Web 框架，以及它们如何简化构建复杂交互式应用程序的过程，同时保持你在这里学到的核心原则。
